﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using alib.Concurrency;
using alib.Debugging;
using alib.Enumerable;

namespace agree
{
	[Serializable]
	public class Stats
	{
		[NonSerialized]
		IIdentity ctrl;

		public Stats(IIdentity ctrl)
		{
			this.ctrl = ctrl;
			this.GrammarLoad = new _grammar_load(this);
			this.NodeLabeler = new _node_labeler(this);
			this.Morphology = new _morphology(this);
			this.Parsing = new _parsing(this);
			this.Generation = new _generation(this);
			this.Totals = new _totals(this);
		}

		public void Disconnect()
		{
			/// don't hold on to the chart or parse control structure just on account of someone retaining these stats
			ctrl = null;
		}

		//public void Append(Stats other)
		//{
		//    Morphology.Append(other.Morphology);
		//    Parsing.Append(other.Parsing);
		//    Generation.Append(other.Generation);
		//}

		public _node_labeler NodeLabeler;
		public _grammar_load GrammarLoad;
		public _morphology Morphology;
		public _parsing Parsing;
		public _generation Generation;

		[NonSerialized]
		public _system_info SystemInfo;

		[NonSerialized]
		public _totals Totals;

		[Serializable]
		public struct _unification
		{
			public int c_attempted;
			public int c_succeeded;

			public int c_checks_attempted;
			public int c_checks_succeeded;

			public _quick_check QuickCheck;

			public double SuccessRatio { get { return c_succeeded / (double)c_attempted; } }

			//public void Append(_unification other)
			//{
			//    if (other.c_attempted != 0)
			//        Interlocked.Add(ref c_attempted, other.c_attempted);
			//    if (other.c_succeeded != 0)
			//        Interlocked.Add(ref c_succeeded, other.c_succeeded);
			//    if (other.c_checks_attempted != 0)
			//        Interlocked.Add(ref c_checks_attempted, other.c_attempted);
			//    if (other.c_checks_succeeded != 0)
			//        Interlocked.Add(ref c_checks_succeeded, other.c_succeeded);
			//    QuickCheck.Append(other.QuickCheck);
			//}

			[Conditional("STATS")]
			public void Record(bool b_result)
			{
				Interlocked.Increment(ref c_checks_attempted);
				if (b_result)
					Interlocked.Increment(ref c_checks_succeeded);
			}

			[Conditional("STATS")]
			public void Record(Tfs tfs)
			{
				Interlocked.Increment(ref c_attempted);
				if (tfs != null)
					Interlocked.Increment(ref c_succeeded);
			}
		};

		[Serializable]
		public struct _grammar_load
		{
			[NonSerialized]
			Stats s;
			public _grammar_load(Stats s)
			{
				this.s = s;
				Unification = default(_unification);
			}
			public _unification Unification;
		};

		[Serializable]
		public struct _node_labeler
		{
			[NonSerialized]
			Stats s;
			public _node_labeler(Stats s)
			{
				this.s = s;
				Unification = default(_unification);
			}
			public _unification Unification;
		};

		[Serializable]
		public struct _quick_check
		{
			public int c_evaluated;
			public int c_avoided;

			public void Append(_quick_check other)
			{
				if (other.c_evaluated != 0)
					Interlocked.Add(ref c_evaluated, other.c_evaluated);
				if (other.c_avoided != 0)
					Interlocked.Add(ref c_avoided, other.c_avoided);
			}

			public double PercentAvoided { get { return c_evaluated == 0 ? double.NaN : c_avoided / (double)c_evaluated; } }

			public void Record(bool b)
			{
				Interlocked.Increment(ref c_evaluated);
				if (b)
					Interlocked.Increment(ref c_avoided);
			}
		};

		[Serializable]
		public struct _morphology
		{
			[NonSerialized]
			Stats s;
			public _morphology(Stats s)
				: this()
			{
				this.s = s;
				this.Unification = default(_unification);
			}
			public _unification Unification;

			//public int c_lexical_transforms;
			public int c_tokens_in;
			public int c_morph_objs_to_chart;
			public long ms_time;

			//public void Append(_morphology other)
			//{
			//    if (other.c_lexical_transforms != 0)
			//        Interlocked.Add(ref c_lexical_transforms, other.c_lexical_transforms);
			//    if (other.c_tokens_to_chart != 0)
			//        Interlocked.Add(ref c_tokens_to_chart, other.c_tokens_to_chart);
			//    if (other.ms_time != 0)
			//        Interlocked.Add(ref ms_time, other.ms_time);
			//    Unification.Append(other.Unification);
			//}

			public double TimeRatio { get { return (double)ms_time / s.Totals.ms_time; } }

			//public void SetComplete() { ms_time = s.ctrl.timer.ElapsedMilliseconds; }
		};

		[Serializable]
		public struct _chart
		{
			public _chart(Stats s)
				: this()
			{
				this.Unification = default(_unification);
				this.Packing = default(_packing);
				this.Unpacking = new _unpacking(s);
			}
			public int c_passive_edges;
			public int c_active_edges;
			public int c_root_edges;
			public int c_root_derivations;
			public int c_rules_filter;
			public int c_edge_geometry_filter;

			public _unification Unification;
			public _packing Packing;
			public _unpacking Unpacking;

			//public long ms_time { get { return _ms_time - s.Morphology.ms_time; } }

			//public double TimeRatio { get { return (double)ms_time / s.Totals.ms_time; } }

			//long _ms_time;
			//public void SetElapsed(long ms) { _ms_time = ms; }

			//public void Append(_chart other)
			//{
			//    Unification.Append(other.Unification);
			//    Packing.Append(other.Packing);
			//    Unpacking.Append(other.Unpacking);

			//    if (other.c_passive_edges != 0)
			//        Interlocked.Add(ref c_passive_edges, other.c_passive_edges);
			//    if (other.c_active_edges != 0)
			//        Interlocked.Add(ref c_active_edges, other.c_active_edges);
			//    if (other.c_root_edges != 0)
			//        Interlocked.Add(ref c_root_edges, other.c_root_edges);
			//    if (other.c_root_derivations != 0)
			//        Interlocked.Add(ref c_root_derivations, other.c_root_derivations);
			//    if (other._ms_time != 0)
			//        Interlocked.Add(ref _ms_time, other.ms_time);
			//}

			[Conditional("STATS")]
			public void PassiveEdge() { Interlocked.Increment(ref c_passive_edges); }

			[Conditional("STATS")]
			public void ActiveEdge() { Interlocked.Increment(ref c_active_edges); }

			[Conditional("STATS")]
			public void RootEdge() { Interlocked.Increment(ref c_root_edges); }

			[Conditional("STATS")]
			public void RuleFiltered() { Interlocked.Increment(ref c_rules_filter); }

			[Conditional("STATS")]
			public void EdgeGeometryFiltered() { Interlocked.Increment(ref c_edge_geometry_filter); }

		};

		[Serializable]
		public struct _packing
		{
			public int c_tests;
			public int c_proactive;
			public int c_retroactive;
			public int c_equivalence;

			public void Append(_packing other)
			{
				if (other.c_tests != 0)
					Interlocked.Add(ref c_tests, other.c_tests);
				if (other.c_proactive != 0)
					Interlocked.Add(ref c_proactive, other.c_proactive);
				if (other.c_retroactive != 0)
					Interlocked.Add(ref c_retroactive, other.c_retroactive);
				if (other.c_equivalence != 0)
					Interlocked.Add(ref c_equivalence, other.c_equivalence);
			}

			public void RecordEvent(int sb)
			{
				Interlocked.Increment(ref c_tests);
				if (sb == Subsumption.Equivalent)
					Interlocked.Increment(ref c_equivalence);
				else if (sb == Subsumption.FirstSubsumesSecond)
					Interlocked.Increment(ref c_proactive);
				else if (sb == Subsumption.SecondSubsumesFirst)
					Interlocked.Increment(ref c_retroactive);
			}
		};

		[Serializable]
		public struct _unpacking
		{
			public _unpacking(Stats s)
				: this()
			{
				this.Unification = default(_unification);
			}
			//public int c_restricted_parses { get { return s.Chart.c_root_derivations; } }
			//public int c_rejected_derivations { get { return c_restricted_parses - c_derivations; } }
			public int c_derivations;
			//public long ms_time { get { return s.Totals.ms_time - s.Parsing.Chart.ms_time - s.Morphology.ms_time; } }
			//public double TimeRatio { get { return (double)ms_time / s.Totals.ms_time; } }

			public _unification Unification;

			//public void Append(_unpacking other)
			//{
			//    Unification.Append(other.Unification);
			//}
		};

		[Serializable]
		public struct _parsing
		{
			[NonSerialized]
			Stats s;
			public _parsing(Stats s)
			{
				this.s = s;
				this.Chart = new _chart(s);
			}
			public _chart Chart;
			//public void Append(_parsing other)
			//{
			//    this.Chart.Append(other.Chart);
			//    Chart.Append(other.Chart);
			//}
		};

		[Serializable]
		public struct _generation
		{
			Stats s;
			public _generation(Stats s)
			{
				this.s = s;
				this.Chart = new _chart(s);
				this.index_accessibility_rejections = 0;
			}
			public _chart Chart;

			public int index_accessibility_rejections;

			//public void Append(_generation other)
			//{
			//    Chart.Append(other.Chart);
			//}
		};

		public struct _system_info
		{
			Stats s;
			public _system_info(Stats s)
			{
				this.s = s;
			}

			public int ProcessorCount
			{
				get { return Environment.ProcessorCount; }
			}

			public bool Is64Bit
			{
				get { return Environment.Is64BitProcess; }
			}
		};

		public struct _totals
		{
			Stats s;
			public _totals(Stats s)
			{
				this.s = s;
				this.ms_time = 0;
				this.Unification = new _unification(s);
			}

			public _unification Unification;

			public long ms_time;

			public double sec_time { get { return ms_time / 1000.0; } }

			public struct _unification
			{
				Stats s;
				public _unification(Stats s)
				{
					this.s = s;
					//this.QuickCheck = new _quick_check(s);
				}

				//				public _quick_check QuickCheck;

				public int c_attempted
				{
					get
					{
						if (s == null)
							return 0;
						return s.Morphology.Unification.c_attempted +
								s.Parsing.Chart.Unification.c_attempted +
								s.Parsing.Chart.Unpacking.Unification.c_attempted +
								s.Generation.Chart.Unification.c_attempted +
								s.Generation.Chart.Unpacking.Unification.c_attempted;
					}
				}
				public int c_succeeded
				{
					get
					{
						return s.Morphology.Unification.c_succeeded +
								s.Parsing.Chart.Unification.c_succeeded +
								s.Parsing.Chart.Unpacking.Unification.c_succeeded +
								s.Generation.Chart.Unification.c_succeeded +
								s.Generation.Chart.Unpacking.Unification.c_succeeded;
					}
				}

				//public struct _quick_check
				//{
				//    Stats s;
				//    public _quick_check(Stats s)
				//    {
				//        this.s = s;
				//    }
				//    public int c_evaluated
				//    {
				//        get
				//        {
				//            return s.Morphology.Unification.QuickCheck.c_evaluated +
				//                    s.Parsing.Unification.QuickCheck.c_evaluated;
				//        }
				//    }
				//    public int c_avoided
				//    {
				//        get
				//        {
				//            return s.Morphology.Unification.QuickCheck.c_avoided +
				//                    s.Parsing.Unification.QuickCheck.c_avoided;
				//        }
				//    }
				//};
			};
		}
	};
}